﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
using SkinnedModel;

namespace zombie2D
{
    class Player
    {
        Model box;

        public int forwardMotion;
        public int headRotation;
        public float speed;
        public float rotation;
        public String modelContent;
        public Model model;
        public Vector3 position;
        public float rotationSpeed;
        public float yaw;
        public float forwardSpeed;

        public SkinningData skinningData;
        public Matrix[] boneTransforms;
        public AnimationPlayer animationPlayer;

        // Set distance from the camera of the near and far clipping planes.
        //static float nearClip = 5.0f;
        static float nearClip = 1.0f;
        static float farClip = 2000.0f;
        Vector3 thirdPersonReference = new Vector3(0, 200, -200);
        // Set field of view of the camera in radians (pi/4 is 45 degrees).
        static float viewAngle = MathHelper.PiOver4;

        public Player(int gamerIndex, ContentManager content, Vector3 position)
        {
            //default model dude.fbx
            this.model = content.Load<Model>("dude");
            //initial starting positiong
           
            this.position = position;
            box = content.Load<Model>("box");

            // Set rates in world units per 1/60th second (the default fixed-step interval).
            this.rotationSpeed = 1f / 60f;
            this.forwardSpeed = 50f / 60f;

            this.loadAnimation();
            
        }
        public void Update()
        {
            //UpdateCameraThirdPerson(graphics);
            this.updateAnimation(this.forwardMotion);
        }
        public void Draw(GraphicsDeviceManager graphics)
        {
            
            UpdateCameraThirdPerson(graphics);
            // Create rotation matrices for the head and arm bones.
            Matrix headTransform = Matrix.CreateRotationX(this.headRotation);
            // Matrix armTransform = Matrix.CreateRotationY(-armRotation);
            Matrix armTransform = Matrix.CreateRotationY(0);

            // Copy the transforms into our own array, so we can safely modify the values.
            this.animationPlayer.GetBoneTransforms().CopyTo(this.boneTransforms, 0);

            //orientation of the whole model root?
            int rootIndex = this.skinningData.BoneIndices["Root"]; //0;
            //make it face away from 3rd person cam...
            this.boneTransforms[rootIndex] = this.boneTransforms[rootIndex] * Matrix.CreateRotationY(-135);

            // Modify the transform matrices for the head and upper-left arm bones.
            int headIndex = this.skinningData.BoneIndices["Head"];
            int armIndex = this.skinningData.BoneIndices["L_UpperArm"];

            this.boneTransforms[headIndex] = headTransform * this.boneTransforms[headIndex];
            this.boneTransforms[armIndex] = armTransform * this.boneTransforms[armIndex];

            // Tell the animation player to recompute the world and skin matrices.
            this.animationPlayer.UpdateWorldTransforms(Matrix.Identity, this.boneTransforms);
            this.animationPlayer.UpdateSkinTransforms();


            
        }
        private void loadAnimation(){
             // Look up our custom skinning information.
            this.skinningData = this.model.Tag as SkinningData;

            if (this.skinningData == null)
                throw new InvalidOperationException
                    ("This model does not contain a SkinningData tag.");

            this.boneTransforms = new Matrix[this.skinningData.BindPose.Count];
            // Create an animation player, and start decoding an animation clip.
            this.animationPlayer = new AnimationPlayer(this.skinningData);
            AnimationClip clip = this.skinningData.AnimationClips["Take 001"];
            this.animationPlayer.StartClip(clip);
        }
        public void updateAnimation(int forwardSpeed)
        {
            TimeSpan advanceTime;
            if (forwardSpeed > 0)
            {
                advanceTime = new TimeSpan(166667);
                // Tell the animation player to compute the latest bone transform matrices.
                this.animationPlayer.UpdateBoneTransforms(advanceTime, true);
            }
            else if (forwardSpeed < 0)
            {
                advanceTime = new TimeSpan(-166667);


                // Tell the animation player to compute the latest bone transform matrices.
                this.animationPlayer.UpdateBoneTransforms(advanceTime, true);
            }else
            {
                advanceTime = TimeSpan.Zero;
            }
            //float headRotation = currentGamePadState.ThumbSticks.Left.X;
            //float armRotation = Math.Max(currentGamePadState.ThumbSticks.Left.Y, 0);
            


        }
        public void updatePosition(KeyboardState keyboardState)
        {
            this.forwardMotion = 0;
            this.headRotation = 0;
            if (keyboardState.IsKeyDown(Keys.Left))
            {
                // Rotate left.
                this.yaw += rotationSpeed;
                this.headRotation = 1;
            }

            if (keyboardState.IsKeyDown(Keys.Right))
            {
                // Rotate right.
                this.yaw -= rotationSpeed;
                this.headRotation = -1;
            }

            if (keyboardState.IsKeyDown(Keys.Up))
            {
                Matrix forwardMovement = Matrix.CreateRotationY(this.yaw);
                Vector3 v = new Vector3(0, 0, forwardSpeed);
                v = Vector3.Transform(v, forwardMovement);
                this.position.Z += v.Z;
                this.position.X += v.X;
                this.forwardMotion = 1;

            }

            if (keyboardState.IsKeyDown(Keys.Down))
            {
                Matrix forwardMovement = Matrix.CreateRotationY(this.yaw);
                Vector3 v = new Vector3(0, 0, -forwardSpeed);
                v = Vector3.Transform(v, forwardMovement);
                this.position.Z += v.Z;
                this.position.X += v.X;
                this.forwardMotion = -1;
            }
            
        }
        void UpdateCameraThirdPerson(GraphicsDeviceManager graphics)
        {
            Matrix view;
            Matrix projection;
            Matrix rotationMatrix = Matrix.CreateRotationY(this.yaw);

            // Create a vector pointing the direction the camera is facing.
            Vector3 transformedReference = Vector3.Transform(thirdPersonReference, rotationMatrix);

            // Calculate the position the camera is looking from.
            Vector3 cameraPosition = transformedReference + this.position;

            // Set up the view matrix and projection matrix.
            view = Matrix.CreateLookAt(cameraPosition, this.position, new Vector3(0.0f, 1f, 0.0f));

            Viewport viewport = graphics.GraphicsDevice.Viewport;
            float aspectRatio = (float)viewport.Width / (float)viewport.Height;

            projection = Matrix.CreatePerspectiveFieldOfView(viewAngle, aspectRatio, nearClip, farClip);

            Matrix world = Matrix.CreateRotationY(this.yaw) * Matrix.CreateTranslation(this.position);


            // bad placement 
            DrawBoxes(view, projection);
            drawSkinModel(this.model, world, projection, view);
        }
        public void drawSkinModel(Model model, Matrix world,Matrix projection,Matrix view)
        {
            Matrix[] bones = this.animationPlayer.GetSkinTransforms();
            foreach (ModelMesh mesh in model.Meshes)
            {
                foreach (SkinnedEffect be in mesh.Effects)
                {
                    be.SetBoneTransforms(bones);
                    be.Projection = projection;
                    be.View = view;
                    be.World = world;
                }
                mesh.Draw();
            }

        }
        public void updatePosition(GamePadState gamePadState)
        {

        }
        
        public String getModelContent(){
            return this.modelContent;
        }
        public void setModel(Model model)
        {
            this.model = model;
        }
        public Model getModel()
        {
            return this.model;
        }
        public float getYaw()
        {
            return this.yaw;
        }
        public int getHeaderRotation()
        {
            return this.headRotation;
        }
        public Vector3 getPosition()
        {
            return this.position;
        }
        #region boxes
        /// <summary>
        /// Draws a field of evenly-spaced 3D boxes.
        /// </summary>
        void DrawBoxes(Matrix view, Matrix proj)
        {
            for (int z = 0; z < 9; z++)
            {
                for (int x = 0; x < 9; x++)
                {
                    DrawModel(box, Matrix.CreateTranslation(x * 60, 0, z * 60), view, proj);
                }
            }
        }

        /// <summary>
        /// Draws the 3D specified model.
        /// </summary>
        /// <param name="model">The 3D model being drawn.</param>
        /// <param name="world">Transformation matrix for world coords.</param>
        /// <param name="texture">Texture used for the drawn 3D model.</param>
        void DrawModel(Model model, Matrix world, Matrix view, Matrix proj)
        {
            foreach (ModelMesh mesh in model.Meshes)
            {
                foreach (BasicEffect be in mesh.Effects)
                {
                    be.Projection = proj;
                    be.View = view;
                    be.World = world;
                    be.TextureEnabled = false;
                }
                mesh.Draw();
            }
        }
        #endregion

    }
}
